package org.mullin.stratego.shared;

public class StateChanger {
	
	/** Result of an attack. */
	public static final int DRAW = 0;
	public static final int ATTACKER_WINS = 1;
	public static final int DEFENDER_WINS = 2;	
	public static final int FLAG_CAPTURED = 3;

	/**
	 * Execute move to provided state. State is updated to reflect move.
	 * 
	 * @param state Current state.
	 * @param move Move to be executed.
	 */
	public void makeMove(State state, Move move) throws IllegalMoveException {
		if (!isValidMove(state, move)) {
			throw new IllegalMoveException();
		}
		
		// transition
		if (!state.getPiece(move.to).equals(State.LAND)) {			
			// attacking
			Piece attacker = state.getPiece(move.from);
			Piece defender = state.getPiece(move.to);
			// reveal attacking pieces
			attacker.setShown(true);
			defender.setShown(true);
			
			switch (getFightWinner(attacker, defender)) {
				case ATTACKER_WINS:
					state.setPiece(attacker, move.to);
				case DEFENDER_WINS:
					state.setPiece(State.LAND, move.from);
					break;
				case DRAW:
					// TODO: what happens here?
					break;
				case FLAG_CAPTURED:
					// game has ended
					state.gameOver();
					break;
				default:
					throw new IllegalMoveException();
			}
		} else {
			// moving to empty space
			Piece p = state.getPiece(move.from);
			state.setPiece(p, move.to);
			state.setPiece(State.LAND, move.from);
		}
		
		state.transition();
	}
	
	/**
	 * Check whether a given move is legal.
	 * 
	 * @param state State of game.
	 * @param move Move being attempted.
	 * @return True if a valid move, false otherwise.
	 */	
	public boolean isValidMove(State state, Move move) {
		// TODO: should this check from also?
		if (!isValidCoord(move.to) || !isValidCoord(move.from)) {
			// move off board
			return false;
		}
		
		Piece movingPiece = state.getPiece(move.from);
		Piece toPiece = state.getPiece(move.to);
		
		if (movingPiece.equals(State.LAND)
				|| movingPiece.equals(State.WATER)) {
			// trying to move an empty spot		
			return false;
		}
				
		if (toPiece.equals(State.WATER)) {
			// move into water
			return false;
		}
				
		// TODO: verify not diagonal
		if (move.to.distanceFrom(move.from) > 1 
				&& !movingPiece.rank.equals(Rank.SCOUT)) {
			// non scout trying to move more than 1 square
			return false;
		}
		
		if (move.to.equals(move.from)) {
			// move to same spot
			return false;
		}
		
		// TODO: verify scout moves in a straight line
		if (movingPiece.rank.equals(Rank.BOMB) || movingPiece.rank.equals(Rank.FLAG)) {
			// trying to move a flag or bomb
			return false;
		}
				
		if (movingPiece.color == toPiece.color) {
			// attacking own piece
			return false;
		}
		
		return true;
	}
	
	/**
	 * Check whether a coordinate lies within a board.
	 * 
	 * @param c Coord to check.
	 * @return True if c is on board, false otherwise.
	 */
	public boolean isValidCoord(Coord c) {
		return c.x >= 0 && c.x < State.COLS 
				&& c.y >= 0 && c.y < State.ROWS;
	}
	
	/**
	 * Determine which piece wins in a fight.
	 * 
	 * @param attacker Attacking piece.
	 * @param defender Defending piece.
	 * @return Which piece wins or draw.
	 */
	public int getFightWinner(Piece attacker, Piece defender) {
		
		if (defender.rank.equals(Rank.BOMB)) {
			// bomb defeats all but sappers
			if (attacker.rank.equals(Rank.SAPPER)) {
				return ATTACKER_WINS;
			} else {
				return DEFENDER_WINS;
			}
		}
		
		if (defender.rank.equals(Rank.MARSHAL)) {
			if (attacker.rank.equals(Rank.SPY)) {
				// spy defeats marshal if it opens on the marshal
				return ATTACKER_WINS;
			} else {
				return DEFENDER_WINS;
			}
		}
		
		if (defender.rank.equals(Rank.FLAG)) {
			return FLAG_CAPTURED;
		}
		
		 int diff = attacker.rank.compareTo(defender.rank);
		 if (diff < 0) {
			 // attacker < defender
			 return DEFENDER_WINS;
		 } else if (diff == 0) {
			 // attacker == defender - attacker wins
			 return ATTACKER_WINS;
		 } else {
			 // attacker > defender
			 return ATTACKER_WINS;
		 }
	}
}
